4.1 Why normal distributions are normal

4.1.1 Normal by addition

Any process that adds together random values from the same distribution converges to a normal. Whatever the average value of the source distribution, each sample from it can be thought of as a fluctuation from the average value.

4.1.2 Normal by multiplication

prod(1 + runif(12,0,0.1))
[1] 1.881523
growth <- replicate(10000, prod(1+runif(12,0,0.1)))
dens(growth, norm.comp=TRUE)

Normal by log-multiplication

We get the gaussian distribution back, because adding logs is equilvalent to multiplying the original numbers. So even multiplicative interactions of large deviations can produce Gaussian distributions, once we measure the outcomes on the log scale.

4.1.4 Using Gaussian distributions

4.1.4.1 Ontological justification

Information theory - maximum entropy

Probability density is the rate of change in cululative probabiity.

4.2 A language for describing models

  1. First, we recognize a set of variables we wish to understand. Some of these are observable. We call these data. Others are unobservable things like rates and averages. We call these parameters.

  2. For each variable, we define it either in terms of the other variables or in therms of a probability distirbution. These definitions make it possible to learn about associations between variables.

  3. The combination of variables and their probability distributions defines a joint generative model that can be used both to simulate hypothetical observations as well as analyze real ones.

4.2.1 Redescribing the global tossing model

  • W ~ Binomial(N,p)
  • p ~ Uniform(0,1)

Where W is the observed count of water, N was the total bumber of tosses, and p was the proportion of water on the globe.

A stochastic relationship is just a mapping of a variable or parameter onto a distribution. It is stochastic because no single instance of a variable on the left is known with certainty.

4.3 A gaussian model of height

d <- Howell1
str(d)
'data.frame':   544 obs. of  4 variables:
 $ height: num  152 140 137 157 145 ...
 $ weight: num  47.8 36.5 31.9 53 41.3 ...
 $ age   : num  63 63 65 41 51 35 32 27 19 54 ...
 $ male  : int  1 0 0 1 0 1 0 1 0 1 ...
precis(d)
'data.frame': 544 obs. of 4 variables:

Independent and identically distributed:

iid - independent and identically distributed - indicates that each value has the sam probability function, independent of the other h values and using the same parameters. * It is an epistemological assumption

de Finetti’s theorem - values which are exchangeable can be approximated by mixtures of i.i.d. distributions. Colloquially, exchangeable values can be reordered.

\(h_i\)Normal(\(\mu,\sigma\)) \(\mu\)Normal(178,20) \(\sigma\)~Uniform(0,50)

Whatever your prior, its a very good idea to plot your priors, so you have a sense of the assumption they build into a model.

Following is the mu (average) distribution of height

Following is the sigma (deviation) distribution

All the above is setting up the prior distributions from which your question is to be answered. As you will see by simulating from this distribution, you can see what your choices imply about observable height.

sample_mu <- rnorm(1e4, 178, 20)
sample_sigma <- runif(1e4, 0, 50)
prior_h <- rnorm(1e4, sample_mu, sample_sigma)
dens(prior_h)

Prior predictive simulation is very useful for assigning sensible priors, because it can be quite hard to anticipate how priors influence the observable variables.

Take for example a much flatter and less informative prior for \(\mu\), like \(\mu\)~Normal(178,100)

sample_mu <- rnorm( 1e4 , 178 , 100 )
prior_h <- rnorm( 1e4 , sample_mu , sample_sigma ) 
dens( prior_h ) 

4.3.3 Grid approximation of the posterior distribution

mu.list <- seq( from=140, to=160 , length.out=200 ) 
sigma.list <- seq( from=4 , to=9 , length.out=200 )
post <- expand.grid( mu=mu.list , sigma=sigma.list ) 
post$LL <- sapply( 1:nrow(post) , function(i) sum( dnorm(
                d2$height ,
                mean=post$mu[i] ,
                sd=post$sigma[i] ,
                log=TRUE ) ) )
post$prod <- post$LL + dnorm( post$mu , 178 , 20 , TRUE ) + dunif( post$sigma , 0 , 50 , TRUE )
post$prob <- exp( post$prod - max(post$prod) )

 contour_xyz( post$mu , post$sigma , post$prob )

 image_xyz( post$mu , post$sigma , post$prob )

4.3.4 Sampling from the posterior

Going to do as similar to what occured in the previous chapters. The main difference is there are now two parameters that were used to be estimated.

sample.rows <- sample( 1:nrow(post) , size=1e4 , replace=TRUE , prob=post$prob )
sample.mu <- post$mu[ sample.rows ] 
sample.sigma <- post$sigma[ sample.rows ]
plot( sample.mu , sample.sigma , cex=0.5 , pch=16 , col=col.alpha(rangi2,0.1) )

dens(sample.mu)

dens(sample.sigma)

HPDI(sample.mu)
   |0.89    0.89| 
153.8693 155.1759 
HPDI(sample.sigma)
   |0.89    0.89| 
7.266332 8.195980 

For a Gausisian likelihood and a gaussian prior on \(\sigma\), the postieror distribution is always gaussian as well regardless of sample size. It is the standard deviation \(\mu\) that causes the problem.

 d3 <- sample( d2$height , size=20 )

mu.list <- seq( from=150, to=170 , length.out=200 ) 
sigma.list <- seq( from=4 , to=20 , length.out=200 ) 
post2 <- expand.grid( mu=mu.list , sigma=sigma.list ) 
post2$LL <- sapply( 1:nrow(post2) , function(i)
    sum( dnorm( d3 , mean=post2$mu[i] , sd=post2$sigma[i] ,
log=TRUE ) ) )
post2$prod <- post2$LL + dnorm( post2$mu , 178 , 20 , TRUE ) +
dunif( post2$sigma , 0 , 50 , TRUE )
post2$prob <- exp( post2$prod - max(post2$prod) )
sample2.rows <- sample( 1:nrow(post2) , size=1e4 , replace=TRUE ,
prob=post2$prob )
sample2.mu <- post2$mu[ sample2.rows ] 
sample2.sigma <- post2$sigma[ sample2.rows ] 
plot( sample2.mu , sample2.sigma , cex=0.5 ,
col=col.alpha(rangi2,0.1) , xlab="mu" , ylab="sigma" , pch=16 )

See how the taill at the top of the clouds is distinctly longer.

4.3.5 Finding the posterior distribution with quap

Quadratic approximation. - handy way to quickly make inferences about the shape of the posterior.

The posterior’s peak wwill lie at the maximum a posteriori estimate (MAP).

data("Howell1")
d <- Howell1
d2 <- d[d$age >= 18, ]
flist <- alist(
height ~ dnorm( mu , sigma ) , 
mu ~ dnorm( 178 , 20 ) , 
sigma ~ dunif( 0 , 50 )
)
 m4.1 <- quap( flist , data=d2 )
precis(m4.1)

m4.2 <- quap( alist(
height ~ dnorm( mu , sigma ) , mu ~ dnorm( 178 , 0.1 ) , sigma ~ dunif( 0 , 50 )
) , data=d2 ) 
precis( m4.2 )
NA
vcov(m4.1)
                mu        sigma
mu    0.1697383125 0.0002152049
sigma 0.0002152049 0.0849042150
diag( vcov( m4.1 ) ) 
        mu      sigma 
0.16973831 0.08490421 

Variance - Covariance Matrix Each entry show the correlation, bounded between -1 and + for each pair of parameters. The 1’s indicate a parameter’s correlation with itself.

Since the correlations are near to 0, it tell us nothing about the learning \(\mu\) tells us nothing about \(\sigma\) and visa versa. But this is quite rare more generally.

cov2cor( vcov( m4.1 ) )
               mu       sigma
mu    1.000000000 0.001792659
sigma 0.001792659 1.000000000
post <- extract.samples(m4.1, n = 1e4)
head(post)
precis(post)
quap posterior: 10000 samples from m4.1

You will find that the mean and standard deviation of each column will be very close to the MAP values from before.

For the multivariable sampling rethinking has a convenient function which essentially is as follows.

4.4 Adding a predictor

plot(d2$height ~ d2$weight)

Just verifies the correlation between the height and weight of these two inputs.

4.4.1 Linear Model Stategy

We are going to add an additional pice to the puzzle \(h_i\)Normal(\(\mu,\sigma\)) \(\mu_i=\alpha+\beta(x_i-\hat{x})\) \(\alpha\)Normal(178,20) \(\beta\)Normal(0,10) \(\sigma\)Uniform(0,50)

4.4.1.1 Probability of the data

4.4.1.2 Linear Model

  • The mean \(\mu\) is no longer a parameter to be estimated. Rather as seen in line two of the model \(\mu_i\) is constructed from other parameters, \(\alpha\) and \(\beta\) and the observed x.

  • This is not a stochastic relationship - there is no ~ in it, but rather a = in it because the definition of \(\mu_i\) is deterministic.

The value \(x_i\) is just the weight value on row i. It refers to the same individual as the height value, \(h_i\), on the same row. The prameters \(\alpha\) and \(\beta\) are more mysterious.

Where did the come from? We made them up.. The parameters \(\mu\) and \(\sigma\) are necessary and sufficient to describe a Gaussian distribution. But \(\alpha\) and \(\beta\) are instead devices we invent for manipuating \(\mu\), allowing it to vary systematically across cases in the data.

One way to understand these made up parameters is to think of them as targets of learning. Each parameter is something that must be described in the posterior distribution.

\[ \mu_i=\alpha+\beta(x_i-\bar{x}) \]

Regression asks two questions about the mean’s outcome

  1. What is the expected height when \(x_i=\bar{x}\)?
  2. What is th change in expected height, when \(x_i\) changes by 1 unit?

4.4.1.3 Priors

We know that \(\alpha\) will be the same as \(\mu\)

But lets try to understand \(\beta\), if beta is = to 0 then the weight has no relations to height.

Lets simulate to understand:

set.seed(2971)
N <- 100
a <- rnorm(N, 178, 20)
b <- rnorm(N, 0,10)
plot( NULL , xlim=range(d2$weight) , ylim=c(-100,400) , xlab="weight" , ylab="height" )
abline( h=0 , lty=2 )
abline( h=272 , lty=1 , lwd=0.5 )
mtext( "b ~ dnorm(0,10)" )
xbar <- mean(d2$weight)
for ( i in 1:N ) curve( a[i] + b[i]*(x - xbar) ,
from=min(d2$weight) , to=max(d2$weight) , add=TRUE , col=col.alpha("black",0.2) )

b <- rlnorm( 1e4 , 0 , 1 )
dens( b , xlim=c(0,5) , adj=0.1 )

set.seed(2971)
N <- 100
a <- rnorm( N , 178 , 20 ) 
b <- rlnorm( N , 0 , 1 )

plot( NULL , xlim=range(d2$weight) , ylim=c(-100,400) , xlab="weight" , ylab="height" )
abline( h=0 , lty=2 )
abline( h=272 , lty=1 , lwd=0.5 )
mtext( "b ~ dnorm(0,10)" )
xbar <- mean(d2$weight)
for ( i in 1:N ) curve( a[i] + b[i]*(x - xbar) ,
from=min(d2$weight) , to=max(d2$weight) , add=TRUE , col=col.alpha("black",0.2) )

There is no more a uniquely correct prior than there is a uniquely correct likelihood. Statistical models are machines for inference. Many machines will work, but some work better than others. Priors can be wrong, but only in the same sense that a kind of hammer can be wrong for building a table.

4.4.2 Finding the postrior distribution

# load data again, since it's a long way back library(rethinking)
data(Howell1)
d <- Howell1
d2 <- d[ d$age >= 18 , ]
# define the average weight, x-bar xbar <- mean(d2$weight)
m4.3 <- quap(
alist( height ~ dnorm( mu , sigma ) , 
mu <- a + b*( weight - xbar ) , 
a ~ dnorm( 178 , 20 ) ,
b ~ dlnorm( 0 , 1 ) ,
sigma ~ dunif( 0 , 50 )), data=d2 )
m4.3b <- quap( alist(
height ~ dnorm( mu , sigma ) ,
mu <- a + exp(log_b)*( weight - xbar ), a ~ dnorm( 178 , 100 ) ,
log_b ~ dnorm( 0 , 1 ) ,
sigma ~ dunif( 0 , 50 ) ),
data=d2 )

I emphasize plotting posterior distributions and posterior pre- dictions, instead of attempting to understand a table. Once you become knowledgable about a particular type of model and kind of data, you’ll be able to confidently read tables, at least as long as you remain within a familiar family of model types

Plotting the implications of your models will allow you to inquire about things that are hard to read from tables.

  1. Whether or not the model fitting procedure worked correctly
  2. The absolute magnitude, rather than merely relative magnitude, of a relationship between outcome and predictor
  3. The uncertainty surrounding an average relationship
  4. The uncertainty surrounding the implied predictions of the model, as these are distinct from mere parameter uncertainty

4.4.3.1 Tables of marginal distributions

precis(m4.3)
round(vcov(m4.3),3)
          a     b sigma
a     0.073 0.000 0.000
b     0.000 0.002 0.000
sigma 0.000 0.000 0.037

4.4.3.2 Plotting posterior inference against data

plot( height ~ weight , data=d2 , col=rangi2 ) 
post <- extract.samples( m4.3 )
a_map <- mean(post$a)
b_map <- mean(post$b)
curve( a_map + b_map*(x - xbar) , add=TRUE )

4.4.3.3 Adding uncertainty around the mean

Above we have the mean of the model, but it does a poor job of communicating uncertainy.

post <- extract.samples(m4.3)
post[1:5,]
N <- 352
dN <- d2[ 1:N , ] 
mN <- quap(
alist(
height ~ dnorm( mu , sigma ) ,
mu <- a + b*( weight - mean(weight) ) , a ~ dnorm( 178 , 20 ) ,
b ~ dlnorm( 0 , 1 ) ,
sigma ~ dunif( 0 , 50 )
) , data=dN )
# extract 20 samples from the posterior post <- extract.samples( mN , n=20 )
# display raw data and sample size
plot( dN$weight , dN$height ,
    xlim=range(d2$weight) , ylim=range(d2$height) ,
    col=rangi2 , xlab="weight" , ylab="height" )
mtext(concat("N = ",N))
# plot the lines, with transparency
for ( i in 1:20 )
curve( post$a[i] + post$b[i]*(x-mean(dN$weight)) ,col=col.alpha("black",0.3) , add=TRUE )

4.4.3.4 Plotting regression intervals and contours

post <- extract.samples(m4.3)
mu_at_50 <- post$a + post$b * (50 - xbar)
dens(mu_at_50, col=rangi2, lwd=2, xlab="mu|weight=50")

 HPDI( mu_at_50 , prob=0.89 )
   |0.89    0.89| 
158.5541 159.6593 

Great start with the above, however we need to repeat the calculation for every possible weight

mu <- link( m4.3 ) 
str(mu)
 num [1:1000, 1:352] 157 157 157 157 158 ...
# define sequence of weights to compute predictions for # these values will be on the horizontal axis 
weight.seq <- seq( from=25 , to=70 , by=1 )
# use link to compute mu
# for each sample from posterior
# and for each weight in weight.seq
mu <- link( m4.3 , data=data.frame(weight=weight.seq) ) 
str(mu)
 num [1:1000, 1:46] 138 137 137 137 137 ...
 # use type="n" to hide raw data
plot( height ~ weight , d2 , type="n" )
for ( i in 1:100 )
points( weight.seq , mu[i,] , pch=16 , col=col.alpha(rangi2,0.1) )

# summarize the distribution of mu
mu.mean <- apply( mu , 2 , mean )
mu.HPDI <- apply( mu , 2 , HPDI , prob=0.89 )

# plot raw data
# fading out points to make line and interval more visible 
plot( height ~ weight , data=d2 , col=col.alpha(rangi2,0.5) )

# plot the MAP line, aka the mean mu for each weight 
lines( weight.seq , mu.mean )

# plot a shaded region for 89% HPDI 
shade( mu.HPDI , weight.seq )

To summarize, here’s the recipe for generating predictions and intervals from the posterior of a fit model.

  1. Use link to generate distributions of posterior values for μ. The default behavior of link is to use the original data, so you have to pass it a list of new horizontal axis values you want to plot posterior predictions across.

  2. Use summary functions like mean or HPDI or PI to find averages and lower and upper bounds of μ for each value of the predictor variable.

  3. Finally,use plotting functions like lines and shade to draw the lines and intervals. Or you might plot the distributions of the predictions, or do further numerical calculations with them. It’s really up to you.

The function link is not really very sophisticated. All is doing is using the formula you provided when you fit the model to compute the value of the linear models.

post <- extract.samples(m4.3)
mu.link <- function(weight) post$a + post$b*( weight - xbar ) 
weight.seq <- seq( from=25 , to=70 , by=1 )
mu <- sapply( weight.seq , mu.link )
mu.mean <- apply( mu , 2 , mean )
mu.HPDI <- apply( mu , 2 , HPDI , prob=0.89 )

4.4.3.5 Prediction intervals

sim.height <- sim( m4.3 , data=list(weight=weight.seq) ) 
str(sim.height)
 num [1:1000, 1:46] 144 130 134 136 138 ...
height.PI <- apply( sim.height , 2 , PI , prob=0.89 )

# plot raw data
plot( height ~ weight , d2 , col=col.alpha(rangi2,0.5) )

# draw MAP line
lines( weight.seq , mu.mean )

# draw HPDI region for line 
shade( mu.HPDI , weight.seq )

# draw PI region for simulated heights 
shade( height.PI , weight.seq )

sim.height <- sim( m4.3 , data=list(weight=weight.seq) , n=1e4 ) 
height.PI <- apply( sim.height , 2 , PI , prob=0.89 )

# plot raw data
plot( height ~ weight , d2 , col=col.alpha(rangi2,0.5) )

# draw MAP line
lines( weight.seq , mu.mean )

# draw HPDI region for line 
shade( mu.HPDI , weight.seq )

# draw PI region for simulated heights 
shade( height.PI , weight.seq )

Rolling over own sim:

post <- extract.samples(m4.3)
weight.seq <- 25:70
sim.height <- sapply( weight.seq , function(weight)
    rnorm(
        n=nrow(post) ,
mean=post$a + post$b*( weight - xbar ) ,
sd=post$sigma ) )
height.PI <- apply( sim.height , 2 , PI , prob=0.89 )

4.5 Curve from lines

The most common polynomial regression is a parabolic model of the mean:

\[ \mu_i=\alpha+\beta_ix_i+\beta_2x_i^2 \]

Its the same linear function of x in a linear regresion, just with a little “1” subscript added to the parameter name.

Approximating the posterior is straightforward.

weight.seq <- seq( from=-2.2 , to=2 , length.out=30 )
pred_dat <- list( weight_s=weight.seq , weight_s2=weight.seq^2 ) 
mu <- link( m4.5 , data=pred_dat )
mu.mean <- apply( mu , 2 , mean )
mu.PI <- apply( mu , 2 , PI , prob=0.89 )
sim.height <- sim( m4.5 , data=pred_dat )
height.PI <- apply( sim.height , 2 , PI , prob=0.89 )

quadradic function

plot( height ~ weight_s , d , col=col.alpha(rangi2,0.5) ) 
lines( weight.seq , mu.mean )
shade( mu.PI , weight.seq )
shade( height.PI , weight.seq )

d$weight_s3 <- d$weight_s^3 

m4.6 <- quap(
alist(
height ~ dnorm( mu , sigma ) ,
mu <- a + b1*weight_s + b2*weight_s2 + b3*weight_s3 , a ~ dnorm( 178 , 20 ) ,
b1 ~ dlnorm( 0 , 1 ) ,
b2 ~ dnorm( 0 , 10 ) , 
b3 ~ dnorm( 0 , 10 ) , 
sigma ~ dunif( 0 , 50 )
), data=d )

Cubic

 plot( height ~ weight_s , d , col=col.alpha(rangi2,0.5) , xaxt="n" )
at <- c(-2,-1,0,1,2)
labels <- at*sd(d$weight) + mean(d$weight) 
axis( side=1 , at=at , labels=round(labels,1) )

4.5.2 Splines

data("cherry_blossoms")
d <- cherry_blossoms
precis(d)
'data.frame': 1215 obs. of 5 variables:

Basis function \[ \mu_i=\alpha+w_iB_{i,1}+w_2B_{i,2}+w_3B_{i,3}+... \]

\(B_{i,b}\) is the nth basis function’s value on row i, and the w parameters are corresponding weights foreach. Te parameters act like slopes , adjusting the influence of each basis function on the mean \(\mu_i\). So realy this is just another linear regression ,but with some fancy, synthetic predictor variables. Thes synthetic variables do some really elegant descriptive geocentric work.

LS0tCnRpdGxlOiAiR2VvY2VudHJpYyBNb2RlbHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CmxpYnJhcnkocmV0aGlua2luZykKYGBgCgojIyA0LjEgV2h5IG5vcm1hbCBkaXN0cmlidXRpb25zIGFyZSBub3JtYWwgCgojIyMgNC4xLjEgTm9ybWFsIGJ5IGFkZGl0aW9uCgpBbnkgcHJvY2VzcyB0aGF0IGFkZHMgdG9nZXRoZXIgcmFuZG9tIHZhbHVlcyBmcm9tIHRoZSBzYW1lIGRpc3RyaWJ1dGlvbiBjb252ZXJnZXMgdG8gYSBub3JtYWwuIFdoYXRldmVyIHRoZSBhdmVyYWdlIHZhbHVlIG9mIHRoZSBzb3VyY2UgZGlzdHJpYnV0aW9uLCBlYWNoIHNhbXBsZSBmcm9tIGl0IGNhbiBiZSB0aG91Z2h0IG9mIGFzIGEgZmx1Y3R1YXRpb24gZnJvbSB0aGUgYXZlcmFnZSB2YWx1ZS4gCmBgYHtyfQpwb3MgPC0gcmVwbGljYXRlKDEwMDAsIHN1bShydW5pZigxNiwtMSwxKSkpCnBsb3QocG9zKQpwbG90KGRlbnNpdHkocG9zKSkKYGBgCgojIyMgNC4xLjIgTm9ybWFsIGJ5IG11bHRpcGxpY2F0aW9uCmBgYHtyfQpwcm9kKDEgKyBydW5pZigxMiwwLDAuMSkpCmBgYAoKYGBge3J9Cmdyb3d0aCA8LSByZXBsaWNhdGUoMTAwMDAsIHByb2QoMStydW5pZigxMiwwLDAuMSkpKQpkZW5zKGdyb3d0aCwgbm9ybS5jb21wPVRSVUUpCmBgYAoKYGBge3J9CmJpZyA8LSByZXBsaWNhdGUoMTAwMDAsIHByb2QoMStydW5pZigxMiwwLDAuNSkpKQpzbWFsbCA8LSByZXBsaWNhdGUoMTAwMDAsIHByb2QoMStydW5pZigxMiwwLDAuMDEpKSkKZGVucyhiaWcsbm9ybS5jb21wID0gVFJVRSkKZGVucyhzbWFsbCxub3JtLmNvbXAgPSBUUlVFKQpgYGAKIyMjIE5vcm1hbCBieSBsb2ctbXVsdGlwbGljYXRpb24KYGBge3J9CmxvZy5iaWcgPC0gcmVwbGljYXRlKDEwMDAwLCBsb2cocHJvZCgxK3J1bmlmKDEyLDAsMC41KSkpKQpkZW5zKGxvZy5iaWcsIG5vcm0uY29tcCA9IFRSVUUpCmBgYAoKV2UgZ2V0IHRoZSBnYXVzc2lhbiBkaXN0cmlidXRpb24gYmFjaywgYmVjYXVzZSBhZGRpbmcgbG9ncyBpcyBlcXVpbHZhbGVudCB0byBtdWx0aXBseWluZyB0aGUgb3JpZ2luYWwgbnVtYmVycy4gU28gZXZlbiBtdWx0aXBsaWNhdGl2ZSBpbnRlcmFjdGlvbnMgb2YgbGFyZ2UgZGV2aWF0aW9ucyBjYW4gcHJvZHVjZSBHYXVzc2lhbiBkaXN0cmlidXRpb25zLCBvbmNlIHdlIG1lYXN1cmUgdGhlIG91dGNvbWVzIG9uIHRoZSBsb2cgc2NhbGUuIAoKIyMjIDQuMS40IFVzaW5nIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbnMgCgojIyMjIDQuMS40LjEgT250b2xvZ2ljYWwganVzdGlmaWNhdGlvbiAKCiMjIEluZm9ybWF0aW9uIHRoZW9yeSAtIG1heGltdW0gZW50cm9weSAKYGBge3J9CmN1cnZlKGV4cCgteF4yKSwgZnJvbSA9IC0zLCB0byA9IDMpCmBgYApQcm9iYWJpbGl0eSBkZW5zaXR5IGlzIHRoZSByYXRlIG9mIGNoYW5nZSBpbiBjdWx1bGF0aXZlIHByb2JhYmlpdHkuIAoKIyMjIDQuMiBBIGxhbmd1YWdlIGZvciBkZXNjcmliaW5nIG1vZGVscyAKCjEuIEZpcnN0LCB3ZSByZWNvZ25pemUgYSBzZXQgb2YgdmFyaWFibGVzIHdlIHdpc2ggdG8gdW5kZXJzdGFuZC4gU29tZSBvZiB0aGVzZSBhcmUgb2JzZXJ2YWJsZS4gV2UgY2FsbCB0aGVzZSBkYXRhLiBPdGhlcnMgYXJlIHVub2JzZXJ2YWJsZSB0aGluZ3MgbGlrZSByYXRlcyBhbmQgYXZlcmFnZXMuIFdlIGNhbGwgdGhlc2UgcGFyYW1ldGVycy4gCgoyLiBGb3IgZWFjaCB2YXJpYWJsZSwgd2UgZGVmaW5lIGl0IGVpdGhlciBpbiB0ZXJtcyBvZiB0aGUgb3RoZXIgdmFyaWFibGVzIG9yIGluIHRoZXJtcyBvZiBhIHByb2JhYmlsaXR5IGRpc3RpcmJ1dGlvbi4gVGhlc2UgZGVmaW5pdGlvbnMgbWFrZSBpdCBwb3NzaWJsZSB0byBsZWFybiBhYm91dCBhc3NvY2lhdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMuIAoKMy4gVGhlIGNvbWJpbmF0aW9uIG9mIHZhcmlhYmxlcyBhbmQgdGhlaXIgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9ucyBkZWZpbmVzIGEgam9pbnQgZ2VuZXJhdGl2ZSBtb2RlbCB0aGF0IGNhbiBiZSB1c2VkIGJvdGggdG8gc2ltdWxhdGUgaHlwb3RoZXRpY2FsIG9ic2VydmF0aW9ucyBhcyB3ZWxsIGFzIGFuYWx5emUgcmVhbCBvbmVzLiAKCiMjIyMgNC4yLjEgUmVkZXNjcmliaW5nIHRoZSBnbG9iYWwgdG9zc2luZyBtb2RlbCAKKiBXIH4gQmlub21pYWwoTixwKQoqIHAgfiBVbmlmb3JtKDAsMSkKCldoZXJlIFcgaXMgdGhlIG9ic2VydmVkIGNvdW50IG9mIHdhdGVyLCBOIHdhcyB0aGUgdG90YWwgYnVtYmVyIG9mIHRvc3NlcywgYW5kIHAgd2FzIHRoZSBwcm9wb3J0aW9uIG9mIHdhdGVyIG9uIHRoZSBnbG9iZS4gCgpBIHN0b2NoYXN0aWMgcmVsYXRpb25zaGlwIGlzIGp1c3QgYSBtYXBwaW5nIG9mIGEgdmFyaWFibGUgb3IgcGFyYW1ldGVyIG9udG8gYSBkaXN0cmlidXRpb24uIEl0IGlzIHN0b2NoYXN0aWMgYmVjYXVzZSBubyBzaW5nbGUgaW5zdGFuY2Ugb2YgYSB2YXJpYWJsZSBvbiB0aGUgbGVmdCBpcyBrbm93biB3aXRoIGNlcnRhaW50eS4gCgpgYGB7cn0KCncgPC0gNgpuIDwtIDkKcF9ncmlkIDwtIHNlcShmcm9tID0gMCwgdG8gPSAxLCBsZW5ndGgub3V0ID0gIDEwMCkKcG9zdGVyaW9yIDwtIGRiaW5vbSh3LCBuLCBwX2dyaWQpICogZHVuaWYocF9ncmlkLCAwLDEpCnBvc3RlcmlvciA8LSBwb3N0ZXJpb3IgLyBzdW0ocG9zdGVyaW9yKQpkZW5zKHBvc3RlcmlvcikKYGBgCgoKIyMjIDQuMyBBIGdhdXNzaWFuIG1vZGVsIG9mIGhlaWdodCAKYGBge3J9CmQgPC0gSG93ZWxsMQpzdHIoZCkKYGBgCgpgYGB7cn0KcHJlY2lzKGQpCmBgYAoKYGBge3J9CmQyPC0gZFtkJGFnZSA+PSAxOCwgXQpgYGAKCkluZGVwZW5kZW50IGFuZCBpZGVudGljYWxseSBkaXN0cmlidXRlZDogCgppaWQgLSBpbmRlcGVuZGVudCBhbmQgaWRlbnRpY2FsbHkgZGlzdHJpYnV0ZWQgLSBpbmRpY2F0ZXMgdGhhdCBlYWNoIHZhbHVlIGhhcyB0aGUgc2FtIHByb2JhYmlsaXR5IGZ1bmN0aW9uLCBpbmRlcGVuZGVudCBvZiB0aGUgb3RoZXIgaCB2YWx1ZXMgYW5kIHVzaW5nIHRoZSBzYW1lIHBhcmFtZXRlcnMuICAqIEl0IGlzIGFuIGVwaXN0ZW1vbG9naWNhbCBhc3N1bXB0aW9uIAoKZGUgRmluZXR0aSdzIHRoZW9yZW0gLSB2YWx1ZXMgd2hpY2ggYXJlIGV4Y2hhbmdlYWJsZSBjYW4gYmUgYXBwcm94aW1hdGVkIGJ5IG1peHR1cmVzIG9mIGkuaS5kLiBkaXN0cmlidXRpb25zLiBDb2xsb3F1aWFsbHksIGV4Y2hhbmdlYWJsZSB2YWx1ZXMgY2FuIGJlIHJlb3JkZXJlZC4KCgokaF9pJH5Ob3JtYWwoJFxtdSxcc2lnbWEkKQokXG11JH5Ob3JtYWwoMTc4LDIwKQokXHNpZ21hJH5Vbmlmb3JtKDAsNTApCgpXaGF0ZXZlciB5b3VyIHByaW9yLCBpdHMgYSB2ZXJ5IGdvb2QgaWRlYSB0byBwbG90IHlvdXIgcHJpb3JzLCBzbyB5b3UgaGF2ZSBhIHNlbnNlIG9mIHRoZSBhc3N1bXB0aW9uIHRoZXkgYnVpbGQgaW50byBhIG1vZGVsLiAKCgpGb2xsb3dpbmcgaXMgdGhlIG11IChhdmVyYWdlKSBkaXN0cmlidXRpb24gb2YgaGVpZ2h0IApgYGB7cn0KY3VydmUoZG5vcm0oeCwxNzgsMjApLCBmcm9tPTEwMCwgdG89MjUwKQpgYGAKCkZvbGxvd2luZyBpcyB0aGUgc2lnbWEgKGRldmlhdGlvbikgZGlzdHJpYnV0aW9uIApgYGB7cn0KY3VydmUoZHVuaWYoeCwwLDUwKSwgZnJvbT0tMTAsdG89NjApCmBgYApBbGwgdGhlIGFib3ZlIGlzIHNldHRpbmcgdXAgdGhlIHByaW9yIGRpc3RyaWJ1dGlvbnMgZnJvbSB3aGljaCB5b3VyIHF1ZXN0aW9uIGlzIHRvIGJlIGFuc3dlcmVkLiBBcyB5b3Ugd2lsbCBzZWUgYnkgc2ltdWxhdGluZyBmcm9tIHRoaXMgZGlzdHJpYnV0aW9uLCB5b3UgY2FuIHNlZSB3aGF0IHlvdXIgY2hvaWNlcyBpbXBseSBhYm91dCBvYnNlcnZhYmxlIGhlaWdodC4gCgpgYGB7cn0Kc2FtcGxlX211IDwtIHJub3JtKDFlNCwgMTc4LCAyMCkKc2FtcGxlX3NpZ21hIDwtIHJ1bmlmKDFlNCwgMCwgNTApCnByaW9yX2ggPC0gcm5vcm0oMWU0LCBzYW1wbGVfbXUsIHNhbXBsZV9zaWdtYSkKZGVucyhwcmlvcl9oKQpgYGAKUHJpb3IgcHJlZGljdGl2ZSBzaW11bGF0aW9uIGlzIHZlcnkgdXNlZnVsIGZvciBhc3NpZ25pbmcgc2Vuc2libGUgcHJpb3JzLCBiZWNhdXNlIGl0IGNhbiBiZSBxdWl0ZSBoYXJkIHRvIGFudGljaXBhdGUgaG93IHByaW9ycyBpbmZsdWVuY2UgdGhlIG9ic2VydmFibGUgdmFyaWFibGVzLiAKClRha2UgZm9yIGV4YW1wbGUgYSBtdWNoIGZsYXR0ZXIgYW5kIGxlc3MgaW5mb3JtYXRpdmUgcHJpb3IgZm9yICRcbXUkLCBsaWtlICRcbXUkfk5vcm1hbCgxNzgsMTAwKQoKYGBge3J9CnNhbXBsZV9tdSA8LSBybm9ybSggMWU0ICwgMTc4ICwgMTAwICkKcHJpb3JfaCA8LSBybm9ybSggMWU0ICwgc2FtcGxlX211ICwgc2FtcGxlX3NpZ21hICkgCmRlbnMoIHByaW9yX2ggKSAKYGBgCiMjIyA0LjMuMyBHcmlkIGFwcHJveGltYXRpb24gb2YgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gCgpgYGB7cn0KbXUubGlzdCA8LSBzZXEoIGZyb209MTQwLCB0bz0xNjAgLCBsZW5ndGgub3V0PTIwMCApIApzaWdtYS5saXN0IDwtIHNlcSggZnJvbT00ICwgdG89OSAsIGxlbmd0aC5vdXQ9MjAwICkKcG9zdCA8LSBleHBhbmQuZ3JpZCggbXU9bXUubGlzdCAsIHNpZ21hPXNpZ21hLmxpc3QgKSAKcG9zdCRMTCA8LSBzYXBwbHkoIDE6bnJvdyhwb3N0KSAsIGZ1bmN0aW9uKGkpIHN1bSggZG5vcm0oCiAgICAgICAgICAgICAgICBkMiRoZWlnaHQgLAogICAgICAgICAgICAgICAgbWVhbj1wb3N0JG11W2ldICwKICAgICAgICAgICAgICAgIHNkPXBvc3Qkc2lnbWFbaV0gLAogICAgICAgICAgICAgICAgbG9nPVRSVUUgKSApICkKcG9zdCRwcm9kIDwtIHBvc3QkTEwgKyBkbm9ybSggcG9zdCRtdSAsIDE3OCAsIDIwICwgVFJVRSApICsgZHVuaWYoIHBvc3Qkc2lnbWEgLCAwICwgNTAgLCBUUlVFICkKcG9zdCRwcm9iIDwtIGV4cCggcG9zdCRwcm9kIC0gbWF4KHBvc3QkcHJvZCkgKQpgYGAKCmBgYHtyfQoKIGNvbnRvdXJfeHl6KCBwb3N0JG11ICwgcG9zdCRzaWdtYSAsIHBvc3QkcHJvYiApCmBgYApgYGB7cn0KIGltYWdlX3h5eiggcG9zdCRtdSAsIHBvc3Qkc2lnbWEgLCBwb3N0JHByb2IgKQpgYGAKIyMjIDQuMy40IFNhbXBsaW5nIGZyb20gdGhlIHBvc3RlcmlvciAKR29pbmcgdG8gZG8gYXMgc2ltaWxhciB0byB3aGF0IG9jY3VyZWQgaW4gdGhlIHByZXZpb3VzIGNoYXB0ZXJzLiBUaGUgbWFpbiBkaWZmZXJlbmNlIGlzIHRoZXJlIGFyZSBub3cgdHdvIHBhcmFtZXRlcnMgdGhhdCB3ZXJlIHVzZWQgdG8gYmUgZXN0aW1hdGVkLiAKCmBgYHtyfQpzYW1wbGUucm93cyA8LSBzYW1wbGUoIDE6bnJvdyhwb3N0KSAsIHNpemU9MWU0ICwgcmVwbGFjZT1UUlVFICwgcHJvYj1wb3N0JHByb2IgKQpzYW1wbGUubXUgPC0gcG9zdCRtdVsgc2FtcGxlLnJvd3MgXSAKc2FtcGxlLnNpZ21hIDwtIHBvc3Qkc2lnbWFbIHNhbXBsZS5yb3dzIF0KYGBgCgpgYGB7cn0KcGxvdCggc2FtcGxlLm11ICwgc2FtcGxlLnNpZ21hICwgY2V4PTAuNSAsIHBjaD0xNiAsIGNvbD1jb2wuYWxwaGEocmFuZ2kyLDAuMSkgKQpgYGAKCmBgYHtyfQpkZW5zKHNhbXBsZS5tdSkKZGVucyhzYW1wbGUuc2lnbWEpCmBgYAoKYGBge3J9CkhQREkoc2FtcGxlLm11KQpgYGAKYGBge3J9CkhQREkoc2FtcGxlLnNpZ21hKQpgYGAKCkZvciBhIEdhdXNpc2lhbiBsaWtlbGlob29kIGFuZCBhIGdhdXNzaWFuIHByaW9yIG9uICRcc2lnbWEkLCB0aGUgcG9zdGllcm9yIGRpc3RyaWJ1dGlvbiBpcyBhbHdheXMgZ2F1c3NpYW4gYXMgd2VsbCByZWdhcmRsZXNzIG9mIHNhbXBsZSBzaXplLiBJdCBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uICRcbXUkICB0aGF0IGNhdXNlcyB0aGUgcHJvYmxlbS4gCgpgYGB7cn0KIGQzIDwtIHNhbXBsZSggZDIkaGVpZ2h0ICwgc2l6ZT0yMCApCgptdS5saXN0IDwtIHNlcSggZnJvbT0xNTAsIHRvPTE3MCAsIGxlbmd0aC5vdXQ9MjAwICkgCnNpZ21hLmxpc3QgPC0gc2VxKCBmcm9tPTQgLCB0bz0yMCAsIGxlbmd0aC5vdXQ9MjAwICkgCnBvc3QyIDwtIGV4cGFuZC5ncmlkKCBtdT1tdS5saXN0ICwgc2lnbWE9c2lnbWEubGlzdCApIApwb3N0MiRMTCA8LSBzYXBwbHkoIDE6bnJvdyhwb3N0MikgLCBmdW5jdGlvbihpKQogICAgc3VtKCBkbm9ybSggZDMgLCBtZWFuPXBvc3QyJG11W2ldICwgc2Q9cG9zdDIkc2lnbWFbaV0gLApsb2c9VFJVRSApICkgKQpwb3N0MiRwcm9kIDwtIHBvc3QyJExMICsgZG5vcm0oIHBvc3QyJG11ICwgMTc4ICwgMjAgLCBUUlVFICkgKwpkdW5pZiggcG9zdDIkc2lnbWEgLCAwICwgNTAgLCBUUlVFICkKcG9zdDIkcHJvYiA8LSBleHAoIHBvc3QyJHByb2QgLSBtYXgocG9zdDIkcHJvZCkgKQpzYW1wbGUyLnJvd3MgPC0gc2FtcGxlKCAxOm5yb3cocG9zdDIpICwgc2l6ZT0xZTQgLCByZXBsYWNlPVRSVUUgLApwcm9iPXBvc3QyJHByb2IgKQpzYW1wbGUyLm11IDwtIHBvc3QyJG11WyBzYW1wbGUyLnJvd3MgXSAKc2FtcGxlMi5zaWdtYSA8LSBwb3N0MiRzaWdtYVsgc2FtcGxlMi5yb3dzIF0gCnBsb3QoIHNhbXBsZTIubXUgLCBzYW1wbGUyLnNpZ21hICwgY2V4PTAuNSAsCmNvbD1jb2wuYWxwaGEocmFuZ2kyLDAuMSkgLCB4bGFiPSJtdSIgLCB5bGFiPSJzaWdtYSIgLCBwY2g9MTYgKQpgYGAKU2VlIGhvdyB0aGUgdGFpbGwgYXQgdGhlIHRvcCBvZiB0aGUgY2xvdWRzIGlzIGRpc3RpbmN0bHkgbG9uZ2VyLgoKYGBge3J9CmRlbnMoc2FtcGxlLnNpZ21hLCBub3JtLmNvbXAgPSBUUlVFKQpgYGAKIyMjIDQuMy41IEZpbmRpbmcgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gd2l0aCBxdWFwIApRdWFkcmF0aWMgYXBwcm94aW1hdGlvbi4gLSBoYW5keSB3YXkgdG8gcXVpY2tseSBtYWtlIGluZmVyZW5jZXMgYWJvdXQgdGhlIHNoYXBlIG9mIHRoZSBwb3N0ZXJpb3IuIAoKVGhlIHBvc3RlcmlvcidzIHBlYWsgd3dpbGwgbGllIGF0IHRoZSBtYXhpbXVtIGEgcG9zdGVyaW9yaSBlc3RpbWF0ZSAoTUFQKS4KCmBgYHtyfQpkYXRhKCJIb3dlbGwxIikKZCA8LSBIb3dlbGwxCmQyIDwtIGRbZCRhZ2UgPj0gMTgsIF0KYGBgCgpgYGB7cn0KZmxpc3QgPC0gYWxpc3QoCmhlaWdodCB+IGRub3JtKCBtdSAsIHNpZ21hICkgLCAKbXUgfiBkbm9ybSggMTc4ICwgMjAgKSAsIApzaWdtYSB+IGR1bmlmKCAwICwgNTAgKQopCmBgYAoKYGBge3J9CiBtNC4xIDwtIHF1YXAoIGZsaXN0ICwgZGF0YT1kMiApCnByZWNpcyhtNC4xKQoKbTQuMiA8LSBxdWFwKCBhbGlzdCgKICBoZWlnaHQgfiBkbm9ybSggbXUgLCBzaWdtYSApICwgCiAgbXUgfiBkbm9ybSggMTc4ICwgMC4xICkgLCAKICBzaWdtYSB+IGR1bmlmKCAwICwgNTAgKQopICwgZGF0YT1kMiApIApwcmVjaXMoIG00LjIgKQoKYGBgCgpgYGB7cn0KdmNvdihtNC4xKQpgYGAKCmBgYHtyfQpkaWFnKCB2Y292KCBtNC4xICkgKSAKYGBgCgpWYXJpYW5jZSAtIENvdmFyaWFuY2UgTWF0cml4IApFYWNoIGVudHJ5IHNob3cgdGhlIGNvcnJlbGF0aW9uLCBib3VuZGVkIGJldHdlZW4gLTEgYW5kICsgIGZvciBlYWNoIHBhaXIgb2YgcGFyYW1ldGVycy4gVGhlIDEncyBpbmRpY2F0ZSBhIHBhcmFtZXRlcidzIGNvcnJlbGF0aW9uIHdpdGggaXRzZWxmLiAKClNpbmNlIHRoZSBjb3JyZWxhdGlvbnMgYXJlIG5lYXIgdG8gMCwgaXQgdGVsbCB1cyBub3RoaW5nIGFib3V0IHRoZSBsZWFybmluZyAkXG11JCB0ZWxscyB1cyBub3RoaW5nIGFib3V0ICRcc2lnbWEkIGFuZCB2aXNhIHZlcnNhLiBCdXQgdGhpcyBpcyBxdWl0ZSByYXJlIG1vcmUgZ2VuZXJhbGx5LiAKYGBge3J9Cgpjb3YyY29yKCB2Y292KCBtNC4xICkgKQpgYGAKCmBgYHtyfQpwb3N0IDwtIGV4dHJhY3Quc2FtcGxlcyhtNC4xLCBuID0gMWU0KQpoZWFkKHBvc3QpCmBgYApgYGB7cn0KcHJlY2lzKHBvc3QpCmBgYAoKWW91IHdpbGwgZmluZCB0aGF0IHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCBjb2x1bW4gd2lsbCBiZSB2ZXJ5IGNsb3NlIHRvIHRoZSBNQVAgdmFsdWVzIGZyb20gYmVmb3JlLiAKCkZvciB0aGUgbXVsdGl2YXJpYWJsZSBzYW1wbGluZyByZXRoaW5raW5nIGhhcyBhIGNvbnZlbmllbnQgZnVuY3Rpb24gd2hpY2ggZXNzZW50aWFsbHkgaXMgYXMgZm9sbG93cy4gCgpgYGB7cn0KIGxpYnJhcnkoTUFTUykKcG9zdCA8LSBtdnJub3JtKCBuPTFlNCAsIG11PWNvZWYobTQuMSkgLCBTaWdtYT12Y292KG00LjEpICkKcHJlY2lzKHBvc3QpCmBgYAojIyA0LjQgQWRkaW5nIGEgcHJlZGljdG9yIAoKYGBge3J9CnBsb3QoZDIkaGVpZ2h0IH4gZDIkd2VpZ2h0KQpgYGAKSnVzdCB2ZXJpZmllcyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgaGVpZ2h0IGFuZCB3ZWlnaHQgb2YgdGhlc2UgdHdvIGlucHV0cy4gCgojIyMgNC40LjEgTGluZWFyIE1vZGVsIFN0YXRlZ3kgCldlIGFyZSBnb2luZyB0byBhZGQgYW4gYWRkaXRpb25hbCBwaWNlIHRvIHRoZSBwdXp6bGUgCiRoX2kkfk5vcm1hbCgkXG11LFxzaWdtYSQpCiRcbXVfaT1cYWxwaGErXGJldGEoeF9pLVxoYXR7eH0pJAokXGFscGhhJH5Ob3JtYWwoMTc4LDIwKQokXGJldGEkfk5vcm1hbCgwLDEwKQokXHNpZ21hJH5Vbmlmb3JtKDAsNTApCgojIyMjIDQuNC4xLjEgUHJvYmFiaWxpdHkgb2YgdGhlIGRhdGEgCiMjIyMgNC40LjEuMiBMaW5lYXIgTW9kZWwgCiogVGhlIG1lYW4gJFxtdSQgaXMgbm8gbG9uZ2VyIGEgcGFyYW1ldGVyIHRvIGJlIGVzdGltYXRlZC4gIFJhdGhlciBhcyBzZWVuIGluIGxpbmUgdHdvIG9mIHRoZSBtb2RlbCAkXG11X2kkIGlzIGNvbnN0cnVjdGVkIGZyb20gb3RoZXIgcGFyYW1ldGVycywgJFxhbHBoYSQgYW5kICRcYmV0YSQgYW5kIHRoZSBvYnNlcnZlZCB4LiAKCiogVGhpcyBpcyBub3QgYSBzdG9jaGFzdGljIHJlbGF0aW9uc2hpcCAtIHRoZXJlIGlzIG5vIH4gaW4gaXQsIGJ1dCByYXRoZXIgYSA9IGluIGl0IGJlY2F1c2UgdGhlIGRlZmluaXRpb24gb2YgJFxtdV9pJCBpcyBkZXRlcm1pbmlzdGljLiAKCgpUaGUgdmFsdWUgJHhfaSQgaXMganVzdCB0aGUgd2VpZ2h0IHZhbHVlIG9uIHJvdyBpLiBJdCByZWZlcnMgdG8gdGhlIHNhbWUgaW5kaXZpZHVhbCBhcyB0aGUgaGVpZ2h0IHZhbHVlLCAkaF9pJCwgb24gdGhlIHNhbWUgcm93LiBUaGUgcHJhbWV0ZXJzICRcYWxwaGEkIGFuZCAkXGJldGEkIGFyZSBtb3JlIG15c3RlcmlvdXMuIAoKV2hlcmUgZGlkIHRoZSBjb21lIGZyb20/IApXZSBtYWRlIHRoZW0gdXAuLiBUaGUgcGFyYW1ldGVycyAkXG11JCBhbmQgJFxzaWdtYSQgYXJlIG5lY2Vzc2FyeSBhbmQgc3VmZmljaWVudCB0byBkZXNjcmliZSBhIEdhdXNzaWFuIGRpc3RyaWJ1dGlvbi4gQnV0ICRcYWxwaGEkIGFuZCAkXGJldGEkIGFyZSBpbnN0ZWFkIGRldmljZXMgd2UgaW52ZW50IGZvciBtYW5pcHVhdGluZyAkXG11JCwgYWxsb3dpbmcgaXQgdG8gdmFyeSBzeXN0ZW1hdGljYWxseSBhY3Jvc3MgY2FzZXMgaW4gdGhlIGRhdGEuIAoKT25lIHdheSB0byB1bmRlcnN0YW5kIHRoZXNlIG1hZGUgdXAgcGFyYW1ldGVycyBpcyB0byB0aGluayBvZiB0aGVtIGFzIHRhcmdldHMgb2YgbGVhcm5pbmcuIEVhY2ggcGFyYW1ldGVyIGlzIHNvbWV0aGluZyB0aGF0IG11c3QgYmUgZGVzY3JpYmVkIGluIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uLgoKJCQKXG11X2k9XGFscGhhK1xiZXRhKHhfaS1cYmFye3h9KQokJAoKUmVncmVzc2lvbiBhc2tzIHR3byBxdWVzdGlvbnMgYWJvdXQgdGhlIG1lYW4ncyBvdXRjb21lIAoKMS4gV2hhdCBpcyB0aGUgZXhwZWN0ZWQgaGVpZ2h0IHdoZW4gJHhfaT1cYmFye3h9JD8gCjIuIFdoYXQgaXMgdGggY2hhbmdlIGluIGV4cGVjdGVkIGhlaWdodCwgd2hlbiAkeF9pJCBjaGFuZ2VzIGJ5IDEgdW5pdD8gCgojIyMjIDQuNC4xLjMgUHJpb3JzIApXZSBrbm93IHRoYXQgJFxhbHBoYSQgd2lsbCBiZSB0aGUgc2FtZSBhcyAkXG11JCAKCkJ1dCBsZXRzIHRyeSB0byB1bmRlcnN0YW5kICRcYmV0YSQsIGlmIGJldGEgaXMgPSAgdG8gMCB0aGVuIHRoZSB3ZWlnaHQgaGFzIG5vIHJlbGF0aW9ucyB0byBoZWlnaHQuIAoKTGV0cyBzaW11bGF0ZSB0byB1bmRlcnN0YW5kOiAKCmBgYHtyfQojIEdlbmVyYXRlIHNpbXVsYXRpb24gZGF0YQpzZXQuc2VlZCgyOTcxKQpOIDwtIDEwMAphIDwtIHJub3JtKE4sIDE3OCwgMjApCmIgPC0gcm5vcm0oTiwgMCwxMCkKYGBgCgpgYGB7cn0KcGxvdCggTlVMTCAsIHhsaW09cmFuZ2UoZDIkd2VpZ2h0KSAsIHlsaW09YygtMTAwLDQwMCkgLCB4bGFiPSJ3ZWlnaHQiICwgeWxhYj0iaGVpZ2h0IiApCmFibGluZSggaD0wICwgbHR5PTIgKQphYmxpbmUoIGg9MjcyICwgbHR5PTEgLCBsd2Q9MC41ICkKbXRleHQoICJiIH4gZG5vcm0oMCwxMCkiICkKeGJhciA8LSBtZWFuKGQyJHdlaWdodCkKZm9yICggaSBpbiAxOk4gKSBjdXJ2ZSggYVtpXSArIGJbaV0qKHggLSB4YmFyKSAsCmZyb209bWluKGQyJHdlaWdodCkgLCB0bz1tYXgoZDIkd2VpZ2h0KSAsIGFkZD1UUlVFICwgY29sPWNvbC5hbHBoYSgiYmxhY2siLDAuMikgKQpgYGAKCgpgYGB7cn0KYiA8LSBybG5vcm0oIDFlNCAsIDAgLCAxICkKZGVucyggYiAsIHhsaW09YygwLDUpICwgYWRqPTAuMSApCgpgYGAKYGBge3J9CnNldC5zZWVkKDI5NzEpCk4gPC0gMTAwCmEgPC0gcm5vcm0oIE4gLCAxNzggLCAyMCApIApiIDwtIHJsbm9ybSggTiAsIDAgLCAxICkKCnBsb3QoIE5VTEwgLCB4bGltPXJhbmdlKGQyJHdlaWdodCkgLCB5bGltPWMoLTEwMCw0MDApICwgeGxhYj0id2VpZ2h0IiAsIHlsYWI9ImhlaWdodCIgKQphYmxpbmUoIGg9MCAsIGx0eT0yICkKYWJsaW5lKCBoPTI3MiAsIGx0eT0xICwgbHdkPTAuNSApCm10ZXh0KCAiYiB+IGRub3JtKDAsMTApIiApCnhiYXIgPC0gbWVhbihkMiR3ZWlnaHQpCmZvciAoIGkgaW4gMTpOICkgY3VydmUoIGFbaV0gKyBiW2ldKih4IC0geGJhcikgLApmcm9tPW1pbihkMiR3ZWlnaHQpICwgdG89bWF4KGQyJHdlaWdodCkgLCBhZGQ9VFJVRSAsIGNvbD1jb2wuYWxwaGEoImJsYWNrIiwwLjIpICkKYGBgClRoZXJlIGlzIG5vIG1vcmUgYSB1bmlxdWVseSBjb3JyZWN0IHByaW9yIHRoYW4gdGhlcmUgaXMgYSB1bmlxdWVseSBjb3JyZWN0IGxpa2VsaWhvb2QuIFN0YXRpc3RpY2FsIG1vZGVscyBhcmUgbWFjaGluZXMgZm9yIGluZmVyZW5jZS4gTWFueSBtYWNoaW5lcyB3aWxsIHdvcmssIGJ1dCBzb21lIHdvcmsgYmV0dGVyIHRoYW4gb3RoZXJzLiBQcmlvcnMgY2FuIGJlIHdyb25nLCBidXQgb25seSBpbiB0aGUgc2FtZSBzZW5zZSB0aGF0IGEga2luZCBvZiBoYW1tZXIgY2FuIGJlIHdyb25nIGZvciBidWlsZGluZyBhIHRhYmxlLiAKCiMjIyA0LjQuMiBGaW5kaW5nIHRoZSBwb3N0cmlvciBkaXN0cmlidXRpb24gCgpgYGB7cn0KIyBsb2FkIGRhdGEgYWdhaW4sIHNpbmNlIGl0J3MgYSBsb25nIHdheSBiYWNrIGxpYnJhcnkocmV0aGlua2luZykKZGF0YShIb3dlbGwxKQpkIDwtIEhvd2VsbDEKZDIgPC0gZFsgZCRhZ2UgPj0gMTggLCBdCiMgZGVmaW5lIHRoZSBhdmVyYWdlIHdlaWdodCwgeC1iYXIgeGJhciA8LSBtZWFuKGQyJHdlaWdodCkKbTQuMyA8LSBxdWFwKAphbGlzdCggaGVpZ2h0IH4gZG5vcm0oIG11ICwgc2lnbWEgKSAsIAptdSA8LSBhICsgYiooIHdlaWdodCAtIHhiYXIgKSAsIAphIH4gZG5vcm0oIDE3OCAsIDIwICkgLApiIH4gZGxub3JtKCAwICwgMSApICwKc2lnbWEgfiBkdW5pZiggMCAsIDUwICkpLCBkYXRhPWQyICkKYGBgCgoKYGBge3J9Cm00LjNiIDwtIHF1YXAoIGFsaXN0KApoZWlnaHQgfiBkbm9ybSggbXUgLCBzaWdtYSApICwKbXUgPC0gYSArIGV4cChsb2dfYikqKCB3ZWlnaHQgLSB4YmFyICksIGEgfiBkbm9ybSggMTc4ICwgMTAwICkgLApsb2dfYiB+IGRub3JtKCAwICwgMSApICwKc2lnbWEgfiBkdW5pZiggMCAsIDUwICkgKSwKZGF0YT1kMiApCmBgYAoKSSBlbXBoYXNpemUgcGxvdHRpbmcgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgYW5kIHBvc3RlcmlvciBwcmUtIGRpY3Rpb25zLCBpbnN0ZWFkIG9mIGF0dGVtcHRpbmcgdG8gdW5kZXJzdGFuZCBhIHRhYmxlLiBPbmNlIHlvdSBiZWNvbWUga25vd2xlZGdhYmxlIGFib3V0IGEgcGFydGljdWxhciB0eXBlIG9mIG1vZGVsIGFuZCBraW5kIG9mIGRhdGEsIHlvdeKAmWxsIGJlIGFibGUgdG8gY29uZmlkZW50bHkgcmVhZCB0YWJsZXMsIGF0IGxlYXN0IGFzIGxvbmcgYXMgeW91IHJlbWFpbiB3aXRoaW4gYSBmYW1pbGlhciBmYW1pbHkgb2YgbW9kZWwgdHlwZXMKClBsb3R0aW5nIHRoZSBpbXBsaWNhdGlvbnMgb2YgeW91ciBtb2RlbHMgd2lsbCBhbGxvdyB5b3UgdG8gaW5xdWlyZSBhYm91dCB0aGluZ3MgdGhhdCBhcmUgaGFyZCB0byByZWFkIGZyb20gdGFibGVzLgoKKDEpIFdoZXRoZXIgb3Igbm90IHRoZSBtb2RlbCBmaXR0aW5nIHByb2NlZHVyZSB3b3JrZWQgY29ycmVjdGx5CigyKSBUaGUgYWJzb2x1dGUgbWFnbml0dWRlLCByYXRoZXIgdGhhbiBtZXJlbHkgcmVsYXRpdmUgbWFnbml0dWRlLCBvZiBhIHJlbGF0aW9uc2hpcApiZXR3ZWVuIG91dGNvbWUgYW5kIHByZWRpY3RvcgooMykgVGhlIHVuY2VydGFpbnR5IHN1cnJvdW5kaW5nIGFuIGF2ZXJhZ2UgcmVsYXRpb25zaGlwCig0KSBUaGUgdW5jZXJ0YWludHkgc3Vycm91bmRpbmcgdGhlIGltcGxpZWQgcHJlZGljdGlvbnMgb2YgdGhlIG1vZGVsLCBhcyB0aGVzZSBhcmUKZGlzdGluY3QgZnJvbSBtZXJlIHBhcmFtZXRlciB1bmNlcnRhaW50eQoKCiMjIyMgNC40LjMuMSBUYWJsZXMgb2YgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucyAKYGBge3J9CnByZWNpcyhtNC4zKQpyb3VuZCh2Y292KG00LjMpLDMpCmBgYAoKIyMjIyA0LjQuMy4yICBQbG90dGluZyBwb3N0ZXJpb3IgaW5mZXJlbmNlIGFnYWluc3QgZGF0YSAKYGBge3J9CnBsb3QoIGhlaWdodCB+IHdlaWdodCAsIGRhdGE9ZDIgLCBjb2w9cmFuZ2kyICkgCnBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKCBtNC4zICkKYV9tYXAgPC0gbWVhbihwb3N0JGEpCmJfbWFwIDwtIG1lYW4ocG9zdCRiKQpjdXJ2ZSggYV9tYXAgKyBiX21hcCooeCAtIHhiYXIpICwgYWRkPVRSVUUgKQpgYGAKIyMjIyA0LjQuMy4zIEFkZGluZyB1bmNlcnRhaW50eSBhcm91bmQgdGhlIG1lYW4gCkFib3ZlIHdlIGhhdmUgdGhlIG1lYW4gb2YgdGhlIG1vZGVsLCBidXQgaXQgZG9lcyBhIHBvb3Igam9iIG9mIGNvbW11bmljYXRpbmcgdW5jZXJ0YWlueS4gCgpgYGB7cn0KcG9zdCA8LSBleHRyYWN0LnNhbXBsZXMobTQuMykKcG9zdFsxOjUsXQpgYGAKCmBgYHtyfQpOIDwtIDM1MgpkTiA8LSBkMlsgMTpOICwgXSAKbU4gPC0gcXVhcCgKYWxpc3QoCmhlaWdodCB+IGRub3JtKCBtdSAsIHNpZ21hICkgLAptdSA8LSBhICsgYiooIHdlaWdodCAtIG1lYW4od2VpZ2h0KSApICwgYSB+IGRub3JtKCAxNzggLCAyMCApICwKYiB+IGRsbm9ybSggMCAsIDEgKSAsCnNpZ21hIH4gZHVuaWYoIDAgLCA1MCApCikgLCBkYXRhPWROICkKIyBleHRyYWN0IDIwIHNhbXBsZXMgZnJvbSB0aGUgcG9zdGVyaW9yIHBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKCBtTiAsIG49MjAgKQojIGRpc3BsYXkgcmF3IGRhdGEgYW5kIHNhbXBsZSBzaXplCnBsb3QoIGROJHdlaWdodCAsIGROJGhlaWdodCAsCiAgICB4bGltPXJhbmdlKGQyJHdlaWdodCkgLCB5bGltPXJhbmdlKGQyJGhlaWdodCkgLAogICAgY29sPXJhbmdpMiAsIHhsYWI9IndlaWdodCIgLCB5bGFiPSJoZWlnaHQiICkKbXRleHQoY29uY2F0KCJOID0gIixOKSkKIyBwbG90IHRoZSBsaW5lcywgd2l0aCB0cmFuc3BhcmVuY3kKZm9yICggaSBpbiAxOjIwICkKY3VydmUoIHBvc3QkYVtpXSArIHBvc3QkYltpXSooeC1tZWFuKGROJHdlaWdodCkpICxjb2w9Y29sLmFscGhhKCJibGFjayIsMC4zKSAsIGFkZD1UUlVFICkKYGBgCiMjIyMgNC40LjMuNCBQbG90dGluZyByZWdyZXNzaW9uIGludGVydmFscyBhbmQgY29udG91cnMgCmBgYHtyfQpwb3N0IDwtIGV4dHJhY3Quc2FtcGxlcyhtNC4zKQptdV9hdF81MCA8LSBwb3N0JGEgKyBwb3N0JGIgKiAoNTAgLSB4YmFyKQpkZW5zKG11X2F0XzUwLCBjb2w9cmFuZ2kyLCBsd2Q9MiwgeGxhYj0ibXV8d2VpZ2h0PTUwIikKYGBgCgpgYGB7cn0KIEhQREkoIG11X2F0XzUwICwgcHJvYj0wLjg5ICkKYGBgCgpHcmVhdCBzdGFydCB3aXRoIHRoZSBhYm92ZSwgaG93ZXZlciB3ZSBuZWVkIHRvIHJlcGVhdCB0aGUgY2FsY3VsYXRpb24gZm9yIGV2ZXJ5IHBvc3NpYmxlIHdlaWdodCAKYGBge3J9Cm11IDwtIGxpbmsoIG00LjMgKSAKc3RyKG11KQpgYGAKCmBgYHtyfQojIGRlZmluZSBzZXF1ZW5jZSBvZiB3ZWlnaHRzIHRvIGNvbXB1dGUgcHJlZGljdGlvbnMgZm9yICMgdGhlc2UgdmFsdWVzIHdpbGwgYmUgb24gdGhlIGhvcml6b250YWwgYXhpcyAKd2VpZ2h0LnNlcSA8LSBzZXEoIGZyb209MjUgLCB0bz03MCAsIGJ5PTEgKQojIHVzZSBsaW5rIHRvIGNvbXB1dGUgbXUKIyBmb3IgZWFjaCBzYW1wbGUgZnJvbSBwb3N0ZXJpb3IKIyBhbmQgZm9yIGVhY2ggd2VpZ2h0IGluIHdlaWdodC5zZXEKbXUgPC0gbGluayggbTQuMyAsIGRhdGE9ZGF0YS5mcmFtZSh3ZWlnaHQ9d2VpZ2h0LnNlcSkgKSAKc3RyKG11KQpgYGAKCmBgYHtyfQogIyB1c2UgdHlwZT0ibiIgdG8gaGlkZSByYXcgZGF0YQpwbG90KCBoZWlnaHQgfiB3ZWlnaHQgLCBkMiAsIHR5cGU9Im4iICkKZm9yICggaSBpbiAxOjEwMCApCnBvaW50cyggd2VpZ2h0LnNlcSAsIG11W2ksXSAsIHBjaD0xNiAsIGNvbD1jb2wuYWxwaGEocmFuZ2kyLDAuMSkgKQpgYGAKYGBge3J9CiMgc3VtbWFyaXplIHRoZSBkaXN0cmlidXRpb24gb2YgbXUKbXUubWVhbiA8LSBhcHBseSggbXUgLCAyICwgbWVhbiApCm11LkhQREkgPC0gYXBwbHkoIG11ICwgMiAsIEhQREkgLCBwcm9iPTAuODkgKQoKIyBwbG90IHJhdyBkYXRhCiMgZmFkaW5nIG91dCBwb2ludHMgdG8gbWFrZSBsaW5lIGFuZCBpbnRlcnZhbCBtb3JlIHZpc2libGUgCnBsb3QoIGhlaWdodCB+IHdlaWdodCAsIGRhdGE9ZDIgLCBjb2w9Y29sLmFscGhhKHJhbmdpMiwwLjUpICkKCiMgcGxvdCB0aGUgTUFQIGxpbmUsIGFrYSB0aGUgbWVhbiBtdSBmb3IgZWFjaCB3ZWlnaHQgCmxpbmVzKCB3ZWlnaHQuc2VxICwgbXUubWVhbiApCgojIHBsb3QgYSBzaGFkZWQgcmVnaW9uIGZvciA4OSUgSFBESSAKc2hhZGUoIG11LkhQREkgLCB3ZWlnaHQuc2VxICkKYGBgCgpUbyBzdW1tYXJpemUsIGhlcmXigJlzIHRoZSByZWNpcGUgZm9yIGdlbmVyYXRpbmcgcHJlZGljdGlvbnMgYW5kIGludGVydmFscyBmcm9tIHRoZSBwb3N0ZXJpb3Igb2YgYSBmaXQgbW9kZWwuCgooMSkgVXNlIGxpbmsgdG8gZ2VuZXJhdGUgZGlzdHJpYnV0aW9ucyBvZiBwb3N0ZXJpb3IgdmFsdWVzIGZvciDOvC4gVGhlIGRlZmF1bHQgYmVoYXZpb3Igb2YgbGluayBpcyB0byB1c2UgdGhlIG9yaWdpbmFsIGRhdGEsIHNvIHlvdSBoYXZlIHRvIHBhc3MgaXQgYSBsaXN0IG9mIG5ldyBob3Jpem9udGFsIGF4aXMgdmFsdWVzIHlvdSB3YW50IHRvIHBsb3QgcG9zdGVyaW9yIHByZWRpY3Rpb25zIGFjcm9zcy4KCigyKSBVc2Ugc3VtbWFyeSBmdW5jdGlvbnMgbGlrZSBtZWFuIG9yIEhQREkgb3IgUEkgdG8gZmluZCBhdmVyYWdlcyBhbmQgbG93ZXIgYW5kIHVwcGVyIGJvdW5kcyBvZiDOvCBmb3IgZWFjaCB2YWx1ZSBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLgoKKDMpIEZpbmFsbHksdXNlIHBsb3R0aW5nIGZ1bmN0aW9ucyBsaWtlIGxpbmVzIGFuZCBzaGFkZSB0byBkcmF3IHRoZSBsaW5lcyBhbmQgaW50ZXJ2YWxzLiBPciB5b3UgbWlnaHQgcGxvdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcHJlZGljdGlvbnMsIG9yIGRvIGZ1cnRoZXIgbnVtZXJpY2FsIGNhbGN1bGF0aW9ucyB3aXRoIHRoZW0uIEl04oCZcyByZWFsbHkgdXAgdG8geW91LgoKVGhlIGZ1bmN0aW9uIGxpbmsgaXMgbm90IHJlYWxseSB2ZXJ5IHNvcGhpc3RpY2F0ZWQuIEFsbCBpcyBkb2luZyBpcyB1c2luZyB0aGUgZm9ybXVsYSB5b3UgcHJvdmlkZWQgd2hlbiB5b3UgZml0IHRoZSBtb2RlbCB0byBjb21wdXRlIHRoZSB2YWx1ZSBvZiB0aGUgbGluZWFyIG1vZGVscy4gCgpgYGB7ciBIb3cgbGluayAgd29ya3N9CnBvc3QgPC0gZXh0cmFjdC5zYW1wbGVzKG00LjMpCm11LmxpbmsgPC0gZnVuY3Rpb24od2VpZ2h0KSBwb3N0JGEgKyBwb3N0JGIqKCB3ZWlnaHQgLSB4YmFyICkgCndlaWdodC5zZXEgPC0gc2VxKCBmcm9tPTI1ICwgdG89NzAgLCBieT0xICkKbXUgPC0gc2FwcGx5KCB3ZWlnaHQuc2VxICwgbXUubGluayApCm11Lm1lYW4gPC0gYXBwbHkoIG11ICwgMiAsIG1lYW4gKQptdS5IUERJIDwtIGFwcGx5KCBtdSAsIDIgLCBIUERJICwgcHJvYj0wLjg5ICkKYGBgCgoKCjQuNC4zLjUgUHJlZGljdGlvbiBpbnRlcnZhbHMgCmBgYHtyfQpzaW0uaGVpZ2h0IDwtIHNpbSggbTQuMyAsIGRhdGE9bGlzdCh3ZWlnaHQ9d2VpZ2h0LnNlcSkgKSAKc3RyKHNpbS5oZWlnaHQpCmhlaWdodC5QSSA8LSBhcHBseSggc2ltLmhlaWdodCAsIDIgLCBQSSAsIHByb2I9MC44OSApCgojIHBsb3QgcmF3IGRhdGEKcGxvdCggaGVpZ2h0IH4gd2VpZ2h0ICwgZDIgLCBjb2w9Y29sLmFscGhhKHJhbmdpMiwwLjUpICkKCiMgZHJhdyBNQVAgbGluZQpsaW5lcyggd2VpZ2h0LnNlcSAsIG11Lm1lYW4gKQoKIyBkcmF3IEhQREkgcmVnaW9uIGZvciBsaW5lIApzaGFkZSggbXUuSFBESSAsIHdlaWdodC5zZXEgKQoKIyBkcmF3IFBJIHJlZ2lvbiBmb3Igc2ltdWxhdGVkIGhlaWdodHMgCnNoYWRlKCBoZWlnaHQuUEkgLCB3ZWlnaHQuc2VxICkKYGBgCgpgYGB7cn0Kc2ltLmhlaWdodCA8LSBzaW0oIG00LjMgLCBkYXRhPWxpc3Qod2VpZ2h0PXdlaWdodC5zZXEpICwgbj0xZTQgKSAKaGVpZ2h0LlBJIDwtIGFwcGx5KCBzaW0uaGVpZ2h0ICwgMiAsIFBJICwgcHJvYj0wLjg5ICkKCiMgcGxvdCByYXcgZGF0YQpwbG90KCBoZWlnaHQgfiB3ZWlnaHQgLCBkMiAsIGNvbD1jb2wuYWxwaGEocmFuZ2kyLDAuNSkgKQoKIyBkcmF3IE1BUCBsaW5lCmxpbmVzKCB3ZWlnaHQuc2VxICwgbXUubWVhbiApCgojIGRyYXcgSFBESSByZWdpb24gZm9yIGxpbmUgCnNoYWRlKCBtdS5IUERJICwgd2VpZ2h0LnNlcSApCgojIGRyYXcgUEkgcmVnaW9uIGZvciBzaW11bGF0ZWQgaGVpZ2h0cyAKc2hhZGUoIGhlaWdodC5QSSAsIHdlaWdodC5zZXEgKQpgYGAKClJvbGxpbmcgb3ZlciBvd24gc2ltOiAKCmBgYHtyfQpwb3N0IDwtIGV4dHJhY3Quc2FtcGxlcyhtNC4zKQp3ZWlnaHQuc2VxIDwtIDI1OjcwCnNpbS5oZWlnaHQgPC0gc2FwcGx5KCB3ZWlnaHQuc2VxICwgZnVuY3Rpb24od2VpZ2h0KQogICAgcm5vcm0oCiAgICAgICAgbj1ucm93KHBvc3QpICwKbWVhbj1wb3N0JGEgKyBwb3N0JGIqKCB3ZWlnaHQgLSB4YmFyICkgLApzZD1wb3N0JHNpZ21hICkgKQpoZWlnaHQuUEkgPC0gYXBwbHkoIHNpbS5oZWlnaHQgLCAyICwgUEkgLCBwcm9iPTAuODkgKQpgYGAKCgojIyMgNC41IEN1cnZlIGZyb20gbGluZXMgCmBgYHtyfQpsaWJyYXJ5KHJldGhpbmtpbmcpIApkYXRhKEhvd2VsbDEpCmQgPC0gSG93ZWxsMQpzdHIoZCkKcGxvdChkJGhlaWdodCwgZCR3ZWlnaHQpCmBgYAoKVGhlIG1vc3QgY29tbW9uIHBvbHlub21pYWwgcmVncmVzc2lvbiBpcyBhIHBhcmFib2xpYyBtb2RlbCBvZiB0aGUgbWVhbjoKCiQkClxtdV9pPVxhbHBoYStcYmV0YV9peF9pK1xiZXRhXzJ4X2leMgokJAoKSXRzIHRoZSBzYW1lIGxpbmVhciBmdW5jdGlvbiBvZiB4IGluIGEgbGluZWFyIHJlZ3Jlc2lvbiwganVzdCB3aXRoIGEgbGl0dGxlICIxIiBzdWJzY3JpcHQgYWRkZWQgdG8gdGhlIHBhcmFtZXRlciBuYW1lLiAKCkFwcHJveGltYXRpbmcgdGhlIHBvc3RlcmlvciBpcyBzdHJhaWdodGZvcndhcmQuIAoKYGBge3J9CmQkd2VpZ2h0X3MgPC0gKCBkJHdlaWdodCAtIG1lYW4oZCR3ZWlnaHQpICkvc2QoZCR3ZWlnaHQpIApkJHdlaWdodF9zMiA8LSBkJHdlaWdodF9zXjIKbTQuNSA8LSBxdWFwKAphbGlzdCgKaGVpZ2h0IH4gZG5vcm0oIG11ICwgc2lnbWEgKSAsCm11IDwtIGEgKyBiMSp3ZWlnaHRfcyArIGIyKndlaWdodF9zMiAsIGEgfiBkbm9ybSggMTc4ICwgMjAgKSAsCmIxIH4gZGxub3JtKCAwICwgMSApICwKYjIgfiBkbm9ybSggMCAsIDEgKSAsCnNpZ21hIH4gZHVuaWYoIDAgLCA1MCApCiksIGRhdGE9ZCApCgpwcmVjaXMobTQuNSkKYGBgCgpgYGB7cn0Kd2VpZ2h0LnNlcSA8LSBzZXEoIGZyb209LTIuMiAsIHRvPTIgLCBsZW5ndGgub3V0PTMwICkKcHJlZF9kYXQgPC0gbGlzdCggd2VpZ2h0X3M9d2VpZ2h0LnNlcSAsIHdlaWdodF9zMj13ZWlnaHQuc2VxXjIgKSAKbXUgPC0gbGluayggbTQuNSAsIGRhdGE9cHJlZF9kYXQgKQptdS5tZWFuIDwtIGFwcGx5KCBtdSAsIDIgLCBtZWFuICkKbXUuUEkgPC0gYXBwbHkoIG11ICwgMiAsIFBJICwgcHJvYj0wLjg5ICkKc2ltLmhlaWdodCA8LSBzaW0oIG00LjUgLCBkYXRhPXByZWRfZGF0ICkKaGVpZ2h0LlBJIDwtIGFwcGx5KCBzaW0uaGVpZ2h0ICwgMiAsIFBJICwgcHJvYj0wLjg5ICkKYGBgCgoKcXVhZHJhZGljIGZ1bmN0aW9uIApgYGB7cn0KcGxvdCggaGVpZ2h0IH4gd2VpZ2h0X3MgLCBkICwgY29sPWNvbC5hbHBoYShyYW5naTIsMC41KSApIApsaW5lcyggd2VpZ2h0LnNlcSAsIG11Lm1lYW4gKQpzaGFkZSggbXUuUEkgLCB3ZWlnaHQuc2VxICkKc2hhZGUoIGhlaWdodC5QSSAsIHdlaWdodC5zZXEgKQoKYGBgCgpgYGB7cn0KZCR3ZWlnaHRfczMgPC0gZCR3ZWlnaHRfc14zIAoKbTQuNiA8LSBxdWFwKAphbGlzdCgKaGVpZ2h0IH4gZG5vcm0oIG11ICwgc2lnbWEgKSAsCm11IDwtIGEgKyBiMSp3ZWlnaHRfcyArIGIyKndlaWdodF9zMiArIGIzKndlaWdodF9zMyAsIGEgfiBkbm9ybSggMTc4ICwgMjAgKSAsCmIxIH4gZGxub3JtKCAwICwgMSApICwKYjIgfiBkbm9ybSggMCAsIDEwICkgLCAKYjMgfiBkbm9ybSggMCAsIDEwICkgLCAKc2lnbWEgfiBkdW5pZiggMCAsIDUwICkKKSwgZGF0YT1kICkKYGBgCgoKQ3ViaWMKCmBgYHtyfQogcGxvdCggaGVpZ2h0IH4gd2VpZ2h0X3MgLCBkICwgY29sPWNvbC5hbHBoYShyYW5naTIsMC41KSAsIHhheHQ9Im4iICkKYXQgPC0gYygtMiwtMSwwLDEsMikKbGFiZWxzIDwtIGF0KnNkKGQkd2VpZ2h0KSArIG1lYW4oZCR3ZWlnaHQpIApheGlzKCBzaWRlPTEgLCBhdD1hdCAsIGxhYmVscz1yb3VuZChsYWJlbHMsMSkgKQoKYGBgCgojIDQuNS4yIFNwbGluZXMKCmBgYHtyfQpkYXRhKCJjaGVycnlfYmxvc3NvbXMiKQpkIDwtIGNoZXJyeV9ibG9zc29tcwpwcmVjaXMoZCkKYGBgCgpCYXNpcyBmdW5jdGlvbgokJApcbXVfaT1cYWxwaGErd19pQl97aSwxfSt3XzJCX3tpLDJ9K3dfM0Jfe2ksM30rLi4uCiQkCgokQl97aSxifSQgaXMgdGhlIG50aCBiYXNpcyBmdW5jdGlvbidzIHZhbHVlIG9uIHJvdyBpLCBhbmQgdGhlIHcgcGFyYW1ldGVycyBhcmUgY29ycmVzcG9uZGluZyB3ZWlnaHRzIGZvcmVhY2guIFRlIHBhcmFtZXRlcnMgYWN0IGxpa2Ugc2xvcGVzICwgYWRqdXN0aW5nIHRoZSBpbmZsdWVuY2Ugb2YgZWFjaCBiYXNpcyBmdW5jdGlvbiBvbiB0aGUgbWVhbiAkXG11X2kkLiBTbyByZWFseSB0aGlzIGlzIGp1c3QgYW5vdGhlciBsaW5lYXIgcmVncmVzc2lvbiAsYnV0IHdpdGggc29tZSBmYW5jeSwgc3ludGhldGljIHByZWRpY3RvciB2YXJpYWJsZXMuIFRoZXMgc3ludGhldGljIHZhcmlhYmxlcyBkbyBzb21lIHJlYWxseSBlbGVnYW50IGRlc2NyaXB0aXZlIGdlb2NlbnRyaWMgd29yay4g